
package jaxb.handlers;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import jaxb.DataStoreException;

import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
 * This class holds common code to access a JAXB data source.
 */
public abstract class AbstractJAXBHandler {
    
    /**
     * JAXBContext responsible for marshalling and unmarshalling data to/from
     * JAXB data source.
     */
    protected JAXBContext jc;
    
    /**
     * Log4J logger object
     */
    //private transient Logger log;
    
    /**
     * The package name used to identify the data type loaded by this handler.
    */
    private String context;
    
    /**
     * The name of the ObjectFactory class
     */
    private static final String OBJECT_FACTORY_CLASS = ".ObjectFactory";

    /**
     * Creates a new instance of AbstractJAXBHandler with the name of the 
     * context, usually the name of the root class that this handler represents
     * 
     * @param context The unique context name, usually the name of the root class that this handler represents
     * @throws es.sgi.spf.jaxb.common.DataStoreException if there is a problem creating a JAXB context
     */
    public AbstractJAXBHandler(String context) 
    throws DataStoreException {
        try {
//            this.getLog().info("Looking up context:"+context);
            this.context = context;
            
            //this.jc = JAXBContext.newInstance(context);
            this.jc = JAXBContext.newInstance(Class.forName(context+OBJECT_FACTORY_CLASS));
            
        } catch(JAXBException ex) {
            throw new DataStoreException("Error creating JAXBContext:"+ex.getMessage(), ex);
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }
    
    /**
     * Reads an XML file with the stated filename into a tree of JAXB objects
     * 
     * @param filename The path of the file to read
     * @return The root JAXB object
     * @throws es.sgi.spf.jaxb.common.DataStoreException if there is a problem 
     * reading the XML file
     */
    public Object read(String filename)
    throws DataStoreException {
        try {
          //  this.getLog().debug("Reading file:"+filename);
            return jc.createUnmarshaller().unmarshal(new File(filename));
        } catch (JAXBException ex) {
            throw new DataStoreException("Error un-marshalling file:"+filename+"-"+ex.getMessage(), ex);
        }
    }
    
    /**
     * Reads an DOM XML node into a tree of JAXB objects
     * 
     * @param node The DOM XML node to read
     * @return The root JAXB object
     * @throws es.sgi.spf.jaxb.common.DataStoreException if the XML node is unreadable
     */
    public Object read(Node node)
    throws DataStoreException {
        try {
            //this.getLog().debug("Reading node:"+node.getNodeName());
            return jc.createUnmarshaller().unmarshal(node);
        } catch (JAXBException ex) {
            throw new DataStoreException("Error un-marshalling node:"+node+"-"+ex.getMessage(), ex);
        }
    }
    
    /**
     * Reads an inputstream into a tree of JAXB objects
     * @param schemaPath The path of the schema
     * @param in The inputstream to read
     * @return The root JAXB object
     * @throws es.sgi.spf.jaxb.common.DataStoreException if the inputstream is unreadable
     */
    public Object read(InputStream in, String schemaPath)
    throws DataStoreException {
        try {
           // this.getLog().debug("Reading inputstream:"+in);
            
            Object ob = null;
            
            if (schemaPath != null){
                Unmarshaller u = jc.createUnmarshaller();
                SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
                Schema schema = sf.newSchema(new File(schemaPath));
                u.setSchema(schema);
                
                // Creates the tree
                ob = u.unmarshal(in);  
            } else{
                ob = jc.createUnmarshaller().unmarshal(in);  
                System.out.println(ob.getClass().getName());
            }
            return ob;
        } catch (JAXBException ex) {
            throw new DataStoreException("Error un-marshalling inputstream:"+ex.getMessage(), ex);
        } catch (IllegalArgumentException ex) {
            throw new DataStoreException("Error un-marshalling inputstream:"+in+"-"+ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new DataStoreException("Error un-marshalling inputstream:"+ex.getMessage(), ex);
        } finally{
            try{
                in.close();
            } catch (IOException ex) {
                throw new DataStoreException("Error un-marshalling inputstream:"+in+"-"+ex.getMessage(), ex);
            }            
        }
    }
    
    /**
     * Writes a tree of JAXB objects to an outputstream
     * 
     * @param object The JAXB object to write
     * @param out The outputstream to write to
     * @throws es.sgi.spf.jaxb.common.DataStoreException if there is a problem writing to the stream
     */
    public void write(Object object, OutputStream out) 
    throws DataStoreException{
        try {
//            this.getLog().debug("Writing object "+object+" to " +
                    //"outputstream:"+out+" using marshaller:"+jc.createMarshaller());
            jc.createMarshaller().marshal(object, out);
            out.flush();
            out.close();
        } catch (JAXBException ex){
             throw new DataStoreException("Error marshalling object to ouputstream:"+ex.getMessage(), ex);
        } catch (IOException ex) {
             throw new DataStoreException("Error marshalling object to ouputstream:"+ex.getMessage(), ex);
        }
    }
    
    /**
     * Returns the context string associated with this handler
     * @return The context
     */ 
    protected String getContext(){
        return context;
    }

}
